package cn.cas.mango.dbscan;

import cn.cas.mango.dto.param.HotKeywords;
import cn.cas.mango.service.GetType;
import co.elastic.clients.elasticsearch._types.SortOrder;
import org.springframework.boot.actuate.endpoint.web.Link;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

/**
 * 簇中的元素类
 */
@lombok.Data
public class Data implements Comparable<Data> {
    private String id;
    private HotKeywords keywords;
    private HotKeywords keywords0;

    private Set<String> entities;
    private Set<String> entitiesPerson;
    private Set<String> entitiesPlace;
    private Set<String> entitiesOrganization;

    private SortOrder sortOrder;
    private String sortField;
    private Date newsPublicDate;
    private Double newsScore;

    private boolean visited;

    private boolean added;
    // 计算是否添加分数
    private boolean addScore;
    private int layer;

  public Date getNewsPublicDate() {
    return newsPublicDate;
  }

  public String getId() {
        return id;
    }

    public boolean isAdded() {
        return added;
    }

    public void setAdded() {
        this.added = true;
    }
    public boolean isVisited() {
        return visited;
    }

    public void setVisited() {
        this.visited = true;
    }

  public HotKeywords getKeywords() {
    return keywords;
  }

  public Data() {
        entities = new LinkedHashSet<>();
        entitiesPerson = new LinkedHashSet<>();
        entitiesPlace = new LinkedHashSet<>();
        entitiesOrganization = new LinkedHashSet<>();
        visited = false;
    }

    public Data(String id) {
        this();
        this.id = id;
    }

    public Data(String id, HotKeywords keywords0, boolean addScore) {
        this(id);
        this.keywords0 = keywords0;
        this.keywords = new HotKeywords(null);
        this.addScore = addScore;
    }

    private void getKeywordsCast(String string, int size) {
      Set<String> temp;
      if (string.equals("person")) {
        temp = entitiesPerson;
      } else if (string.equals("place")) {
        temp = entitiesPlace;
      } else if (string.equals("org")){
        temp = entitiesOrganization;
      } else if (string.equals("keywords")){
        temp = keywords0.getKeywords();
      } else {
        temp = entities;
      }
      int count = 0 ;
      for (String e: temp) {
        keywords.add(e);
        count ++;
        if (count > size) break;
      }
    }
    private void rebuildKeywords(int layer, List<String> strings) {
      if (layer == 0) {
        keywords.addAll(keywords0);
      } else {
        int total = 40 / strings.size();
        for (String string: strings) {
          getKeywordsCast(string, total);
        }
      }
//      Set<String> inter = calInter(keywords.getKeywords(), entities);
//      Set<String> union = calUnion(keywords.getKeywords(), entities);
//      Set<String> add = new LinkedHashSet<>(entities);
//      add.removeAll(inter);
//      add.forEach(x->keywords.add(x));

  }

    public Data(String id, HotKeywords keywords0, List<List<String>> entitiesList,
                String newsPublicDate, Double newsScore,
                SortOrder sortOrder, String sortField, boolean addScore, int layer) {
        this(id, keywords0, addScore);
        entitiesPerson.addAll(entitiesList.get(0));
        entitiesPlace.addAll(entitiesList.get(1));
        entitiesOrganization.addAll(entitiesList.get(2));
        entities.addAll(entitiesList.get(3));
        this.layer = layer;
        rebuildKeywords(layer, new GetType().getFILENAME(layer));
        try {
            this.newsPublicDate = new SimpleDateFormat("yyyy-MM-dd").parse(newsPublicDate);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
        this.newsScore = newsScore;
        this.sortOrder = sortOrder;
        this.sortField = sortField;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Data)) return false;
        Data data = (Data) o;
        return Objects.equals(keywords, data.keywords);
    }

    @Override
    public int hashCode() {
        return Objects.hash(keywords);
    }

    /**
     * 计算相似度
     * @param data2，要与当前类
     * @return
     */
    public double calSim(Data data2) {
        HotKeywords keywords1 = keywords;
        HotKeywords keywords2 = data2.keywords;
        if (keywords.size() == 0 || keywords2.size() == 0) {
          return 0;
        }
        Set<String> interKeywords = calInter(keywords2.getKeywords());
        double sim;
        if (addScore) {
          double pointMultiPly = 0;
          for (String keyword: interKeywords) {
            pointMultiPly += keywords1.get(keyword) * keywords2.get(keyword);
          }
          double norm1 = keywords1.getNorm();
          double norm2 = keywords2.getNorm();
          sim = pointMultiPly / (norm1 * norm2);
        } else {
          Set<String> unionKeywords = calUnion(keywords2.getKeywords());
          sim = interKeywords.size() * 1.0 / unionKeywords.size();
        }
        return sim;
    }

    public static Set<String> calUnion(Set<String> values1, Set<String> value2) {
        Set<String> strings = new LinkedHashSet<>(values1);
        strings.addAll(value2);
        return strings;
    }

    private Set<String> calUnion(Set<String> value2) {
        return calUnion(keywords.getKeywords(), value2);
    }

    private Set<String> calInter(Set<String> values2) {
        return calInter(keywords.getKeywords(), values2);
    }

    public static Set<String> calInter(Set<String> values1, Set<String> values2) {
        Set<String> strings = new LinkedHashSet<>(values1);
        strings.retainAll(values2);
        return strings;
    }

    @Override
    public int compareTo(Data d2) {
        Date date1 = newsPublicDate;
        Date date2 = d2.newsPublicDate;
        int scoreRes = newsScore.compareTo(d2.newsScore);
        int dateRes = date1.compareTo(date2);
        if (sortField.equals("_score")) {
          if (scoreRes == 0) {
            return -dateRes;
          } return scoreRes;
        } else {
          if (SortOrder.Asc.equals(sortOrder)) {
            if (dateRes == 0) {
              return -scoreRes;
            } return dateRes;
          } else {
            if (dateRes == 0) {
              return -scoreRes;
            } return -dateRes;
          }
        }
    }
}
